{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "spanish-hughes",
   "metadata": {},
   "source": [
    "# tf 卷积膨胀测试 与字母轮廓识别\n",
    "水滴算法，行列扫描"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "encouraging-jacket",
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib.font_manager import FontManager\n",
    "import subprocess\n",
    "\n",
    "fm = FontManager()\n",
    "mat_fonts = set(f.name for f in fm.ttflist)\n",
    "output = subprocess.check_output('fc-list :lang=zh -f \"%{family}\\n\"', shell=True)\n",
    "zh_fonts = set(f.split(',', 1)[0] for f in output.decode('utf-8').split('\\n'))\n",
    "available = mat_fonts & zh_fonts\n",
    "print ('*' * 10, '可用的字体', '*' * 10)\n",
    "for f in available:\n",
    "     print (f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dated-installation",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import tensorflow as tf\n",
    "import cv2\n",
    "from PIL import Image\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "plt.rcParams['font.sans-serif']=['SimHei','WenQuanYi Zen Hei']\n",
    "path=\"ocrdata/test.png\"\n",
    "allpath=\"ocrdata/inputdata.png\"\n",
    "screen=Image.open(path)\n",
    "imggrab=np.mean(screen,-1)\n",
    "img=np.where(imggrab[...,:] < 130, 0, 255) # 二值化 #黑色128\n",
    "allscreen=Image.open(allpath)\n",
    "allimggrab=np.mean(allscreen,-1)\n",
    "allimg=np.where(allimggrab[...,:] < 130, 0, 255) # 二值化 #黑色128\n",
    "fig, ax = plt.subplots(1, 3, figsize=(12, 5))\n",
    "ax[0].set_title(\"原图像\")\n",
    "ax[0].imshow(screen);\n",
    "ax[1].set_title(\"灰度（去除通道）\")\n",
    "ax[1].imshow(imggrab);\n",
    "ax[2].set_title(\"二值化\")\n",
    "ax[2].imshow(img);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "norwegian-electricity",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "img"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "micro-baker",
   "metadata": {},
   "outputs": [],
   "source": [
    "def numpy_conv(inputs,filter,padding=\"VALID\"):\n",
    "    H, W = inputs.shape\n",
    "    filter_size = filter.shape[0]\n",
    "    # default np.floor\n",
    "    filter_center = int(filter_size / 2.0)\n",
    "    filter_center_ceil = int(np.ceil(filter_size / 2.0))\n",
    "\n",
    "    #这里先定义一个和输入一样的大空间，但是周围一圈后面会截掉\n",
    "    #更新下新输入,SAME模式下，会改变HW\n",
    "    H, W = inputs.shape\n",
    "    result = np.zeros((H - filter_size + 1,W - filter_size + 1))\n",
    "    #print(\"new size\",H,W)\n",
    "    #卷积核通过输入的每块区域，stride=1，注意输出坐标起始位置\n",
    "    for r in range(0, H - filter_size + 1):\n",
    "        for c in range(0, W - filter_size + 1):\n",
    "            # 池化大小的输入区域\n",
    "            cur_input = inputs[r:r + filter_size,\n",
    "                        c:c + filter_size]\n",
    "            #和核进行乘法计算\n",
    "            cur_output = cur_input * filter\n",
    "            #再把所有值求和\n",
    "            conv_sum = np.sum(cur_output)\n",
    "            #当前点输出值\n",
    "            result[r, c] = conv_sum\n",
    "    return result\n",
    "# tf卷积操作\n",
    "def tf_conv(img,filter,padding='VALID'):\n",
    "    x=tf.constant(img, dtype=tf.float32)\n",
    "    kernel=tf.constant(filter, dtype=tf.float32)\n",
    "    return tf.nn.conv2d(x,kernel, strides=[1, 1, 1, 1],padding=padding)\n",
    "# tf卷积2\n",
    "def tf_corr2d(X, K):\n",
    "    h, w = K.shape\n",
    "    Y = tf.Variable(tf.zeros((X.shape[0] - h + 1, X.shape[1] - w +1)))\n",
    "    for i in range(Y.shape[0]):\n",
    "        for j in range(Y.shape[1]):\n",
    "            Y[i,j].assign(tf.cast(tf.reduce_sum(X[i:i+h, j:j+w] * K), dtype=tf.float32))\n",
    "    return Y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "chief-reproduction",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 裁剪\n",
    "import math\n",
    "def cut(img,boxs):\n",
    "    return [img.crop(i) for box in boxs]\n",
    "def cutrimg(img,boxs):\n",
    "    return [img[box[1]:box[3],box[0]:box[2]] for box in boxs]\n",
    "#显示\n",
    "def debugshowlist(list,num=5,figsize=(12, 5),rule=True):\n",
    "    length=len(list)\n",
    "    linenum=math.ceil(length/num)\n",
    "    fig,ax=plt.subplots(linenum,num,figsize=figsize)\n",
    "    for i,ax in enumerate(ax.flatten()):\n",
    "        if i>=length:\n",
    "            return\n",
    "        img=list[i]\n",
    "        if (isinstance(img,np.ndarray)):\n",
    "            s=Image.fromarray(img)\n",
    "        else:\n",
    "            s=img\n",
    "        if (not rule):\n",
    "            ax.axis('off')\n",
    "        ax.imshow(s);\n",
    "        \n",
    "# 行切割\n",
    "def line_select(rimg,line_heightmin=7): #（二值化img）\n",
    "    line_data=np.where(~img.any(axis=1))[0] #获取所有像素都是空的行 ~反转true false\n",
    "    line_range=[] #获取行域\n",
    "    for y1,y2 in zip(line_data[:-1],line_data[1:]):\n",
    "        if y2-y1>line_heightmin:\n",
    "            line_range.append((y1+1,y2))\n",
    "    if len(line_range)==0:\n",
    "        line_range.append((0,img.shape[0]))\n",
    "    return line_range\n",
    "def line_select_splite(rimg,line_range):\n",
    "    return [img[y1:y2] for y1,y2 in inline_range]\n",
    "# 列切割\n",
    "def col_select(img,col_widthmin=1,y1=0,y2=len(img)):\n",
    "    col_range=[]\n",
    "    col_data=np.where(~img.any(axis=0))[0]\n",
    "    for x1,x2 in zip(col_data[:-1],col_data[1:]):\n",
    "        if x2-x1>col_widthmin:\n",
    "            col_range.append((x1,y1,x2,y2))\n",
    "    return col_range\n",
    "# 格切割\n",
    "def cell_select(img,line_heightmin,col_widthmin):\n",
    "    line_range=line_select(img,line_heightmin)#获取行域\n",
    "    linedata=line_select_splite(img,line_range)#获取行数据\n",
    "    return [col_select(line,col_widthmin,y1,y2) for line,(y1,y2) in zip(linedata,line_range)]\n",
    "#测试分割二值化图片\n",
    "def test_rimg(img):\n",
    "    debugshowlist(cutrimg(img,col_select(img)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "external-october",
   "metadata": {},
   "outputs": [],
   "source": [
    "#卷积 膨胀收缩\n",
    "expand=np.where(numpy_conv(img,np.array([[0,0,0],\n",
    "                                         [1,1,1],\n",
    "                                         [0,0,0]]))[...,:] < 130, 0, 255)\n",
    "shrink=np.where(numpy_conv(img,np.array([[0,0,0,0],\n",
    "                                         [-0.5,1,1,-0.5],\n",
    "                                         [-0.5,1,1,-0.5],\n",
    "                                         [0,0,0,0]]))[...,:] < 130, 0, 255)\n",
    "fig, ax = plt.subplots(1, 3, figsize=(12, 5))\n",
    "ax[0].set_title(\"二值化%s\" % (str(img.shape)))\n",
    "ax[0].imshow(img);\n",
    "ax[1].set_title(\"膨胀%s\" % (str(expand.shape)))\n",
    "ax[1].imshow(expand);\n",
    "ax[2].set_title(\"收缩%s\" % (str(shrink.shape)))\n",
    "ax[2].imshow(shrink);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "undefined-charity",
   "metadata": {},
   "outputs": [],
   "source": [
    "#tf卷积1\n",
    "expand=np.where(tf_corr2d(img,np.array(\n",
    "    [[0,0,0],\n",
    "     [1,1,1],\n",
    "     [0,0,0]]))[...,:] < 130, 0, 255)\n",
    "shrink=np.where(tf_corr2d(img,np.array(\n",
    "    [[0,0,0,0],\n",
    "     [-0.5,1,1,-0.5],\n",
    "     [-0.5,1,1,-0.5],\n",
    "     [0,0,0,0]]))[...,:] < 130, 0, 255)\n",
    "fig, ax = plt.subplots(1, 3, figsize=(12, 5))\n",
    "ax[0].set_title(\"二值化%s\" % (str(img.shape)))\n",
    "ax[0].imshow(img);\n",
    "ax[1].set_title(\"膨胀%s\" % (str(expand.shape)))\n",
    "ax[1].imshow(expand);\n",
    "ax[2].set_title(\"收缩%s\" % (str(shrink.shape)))\n",
    "ax[2].imshow(shrink);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "hollywood-canon",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# cv收缩膨胀\n",
    "#cvimg=cv2.imread(path,0)\n",
    "# cvimg=cv2.cvtColor(np.asarray(screen), cv2.COLOR_RGB2BGR)\n",
    "cvimg=np.asarray(img,np.uint8)\n",
    "#kernel=cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))\n",
    "#kernel = np.ones((5,5),np.uint8)\n",
    "kernel = np.array([[0,0,0,0],\n",
    "     [-0.5,1,1,-0.5],\n",
    "     [-0.5,1,1,-0.5],\n",
    "     [0,0,0,0]],np.uint8)\n",
    "fig, ax = plt.subplots(1, 1, figsize=(12, 5))\n",
    "#原始识别文字轮廓\n",
    "ax.imshow(cvimg);\n",
    "contours, hierarchy=cv2.findContours(cvimg,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)\n",
    "debugshowlist([img[y:y+h,x:x+w]  for x,y,w,h in [cv2.boundingRect(i) for i in contours]],3,rule=False)\n",
    "#腐蚀\n",
    "eroded = cv2.erode(cvimg,kernel)\n",
    "fig, ax = plt.subplots(1, 1, figsize=(12, 5))\n",
    "ax.imshow(eroded);\n",
    "contours, hierarchy=cv2.findContours(eroded,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)\n",
    "debugshowlist([img[y:y+h,x:x+w]  for x,y,w,h in [cv2.boundingRect(i) for i in contours]],3,rule=False)\n",
    "#膨胀\n",
    "fig, ax = plt.subplots(1, 1, figsize=(12, 5))\n",
    "dilate = cv2.dilate(cvimg,kernel)\n",
    "ax.imshow(dilate);\n",
    "contours, hierarchy=cv2.findContours(dilate,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)\n",
    "debugshowlist([img[y:y+h,x:x+w]  for x,y,w,h in [cv2.boundingRect(i) for i in contours]],3,rule=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "unlimited-place",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "fig, ax = plt.subplots(1, 1, figsize=(50, 50))\n",
    "#原始识别文字轮廓\n",
    "cvimg=np.asarray(allimg,np.uint8)\n",
    "ax.imshow(cvimg);\n",
    "# contours, hierarchy=cv2.findContours(cvimg,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)\n",
    "# debugshowlist([allimg[y:y+h,x:x+w]  for x,y,w,h in [cv2.boundingRect(i) for i in contours]],2,figsize=(12, 50),rule=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "moved-steering",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(allimg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "swedish-prompt",
   "metadata": {},
   "outputs": [],
   "source": [
    "test_rimg(shrink);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "flexible-pollution",
   "metadata": {},
   "outputs": [],
   "source": [
    "#收缩后切割\n",
    "calclist=[img]\n",
    "# calc= lambda data :  (print(\"save\"),tf_corr2d(data,np.array(\n",
    "#     [[0,-1,0],\n",
    "#      [-1,5,-1],\n",
    "#      [0,-1,0]])))[-1]\n",
    "def calc(data):\n",
    "    save=tf_corr2d(data,np.array(\n",
    "    [[0,-1,0],\n",
    "     [-1,5,-1],\n",
    "     [0,-1,0]]))\n",
    "    calclist.append(save)\n",
    "    return save\n",
    "binarization = lambda data: np.where(data[...,:] < 130, 0, 255)\n",
    "calclist.append(binarization(calc(calc(calc(imggrab)))))\n",
    "%matplotlib notebook\n",
    "fig,ax=plt.subplots()\n",
    "list=[]\n",
    "for i in range(len(calclist)):\n",
    "    title = ax.text(0.5,1.05,\"Title {}\".format(i), \n",
    "                    size=plt.rcParams[\"axes.titlesize\"],\n",
    "                    ha=\"center\", transform=ax.transAxes, )\n",
    "    list.append([ax.imshow(calclist[i],animated=True),title])\n",
    "print(list)\n",
    "ani = animation.ArtistAnimation(fig,list, interval=200, repeat_delay=1000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "toxic-pricing",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "fig = plt.figure()\n",
    "import matplotlib.animation as animation\n",
    "from IPython.display import HTML\n",
    "y1 = []\n",
    "for i in range(50):\n",
    "    y1.append(plt.imshow(i))  # 每迭代一次，将i放入y1中画出来\n",
    "ani = animation.ArtistAnimation(fig, y1, interval=200, repeat_delay=1000)\n",
    "ani.save(\"git.gif\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "realistic-stroke",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cloudy-timber",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "turned-calgary",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
